<?php

namespace app\controller;
// 定义命名空间为app\controller

use Exception;

// 导入PHP标准异常类
use InvalidArgumentException;
use Qcloud\Cos\Client;
use Random\RandomException;
use RuntimeException;

// 导入腾讯云COS SDK客户端类

/**
 * CosUploader类 - 腾讯云对象存储上传工具
 * 用于处理腾讯云COS文件上传相关操作
 */
class CosUploader
{

    private string $secretId;
    private string $secretKey;
    private string $bucket;    // 定义私有属性：存储桶、地域、密钥ID和密钥
    private string $region;

    /**
     * 构造函数
     * @param string $bucket 存储桶名称
     * @param string $region 区域
     * @param string $secretId 密钥ID
     * @param string $secretKey 密钥内容
     */
    public function __construct(string $bucket, string $region, string $secretId, string $secretKey)
    {
        $this->bucket = $bucket;        // 初始化存储桶名称
        $this->region = $region;        // 初始化区域
        $this->secretId = $secretId;    // 初始化密钥ID
        $this->secretKey = $secretKey;  // 初始化密钥内容
    }

    /**
     * 格式化远程路径为合法的COS对象Key
     * 
     * @param string $remotePath 原始路径
     * @return string 格式化后的合法Key路径
     */
    private function formatRemotePath(string $remotePath): string
    {
        // 确保路径以斜杠开头
        if (!str_starts_with($remotePath, '/')) {
            $remotePath = '/' . $remotePath;
        }
        
        // 移除路径中的多余斜杠
        $remotePath = preg_replace('#/+#', '/', $remotePath);
        
        // 移除开头的斜杠(COS对象Key不需要以/开头)
        //$remotePath = ltrim($remotePath, '/');
        
        // 如果路径为空，返回默认路径
        if (empty($remotePath)) {
            return '/game/product/';
        }
        
        return $remotePath;
    }

    /**
     * 从URL上传文件到COS
     * 
     * @param string $fileUrl 文件URL地址
     * @param string $remotePath COS中的保存路径
     * @return object|bool 成功返回上传结果对象，失败返回false
     */
    public function uploadFileFromUrl(string $fileUrl, string $remotePath): object|bool
    {

        try {
            // 获取系统临时目录（自动适配Linux/Windows）
            $tempDir = sys_get_temp_dir();

            // 解析URL路径并提取纯净的扩展名
            $parsedUrl = parse_url($fileUrl);
            $path = $parsedUrl['path'] ?? $fileUrl;
            $extension = pathinfo($path, PATHINFO_EXTENSION);

            // 生成带随机名的临时文件路径
            $tempFileName = md5(uniqid(random_int(0, mt_getrandmax()), true)) . '.' . $extension;
            $tempFilePath = $tempDir . DIRECTORY_SEPARATOR . $tempFileName;         // 正确拼接路径

            // 获取文件内容
            $fileContent = file_get_contents($fileUrl);
            if ($fileContent === false) return false;

            // 写入文件（添加错误抑制符和严格校验）
            $result = @file_put_contents($tempFilePath, $fileContent, LOCK_EX);
            if ($result === false) {
                throw new RuntimeException("Failed to write temp file: " . error_get_last()['message']);
            }

            // 格式化远程路径
            $remotePath = $this->formatRemotePath($remotePath);
            $remotePathName = $remotePath.$tempFileName;

            $cosClient = new Client([
                'region' => $this->region, // 设置区域
                'credentials' => ['secretId' => $this->secretId, 'secretKey' => $this->secretKey], // 设置认证信息
            ]);

            $result = $cosClient->putObject([
                'Bucket' => $this->bucket,
                'Key' => $remotePathName, // 处理后的合法Key
                'Body' => fopen($tempFilePath, 'rb'),
            ]);
            unlink($tempFilePath); // 删除临时文件
            return $result; // 返回上传结果
        } catch (Exception $e) {
            var_dump($e->getMessage());
            unlink($tempFilePath); // 出错时也删除临时文件
            return false; // 上传失败返回false
        }
    }

    /**
     * 从本地上传文件到COS
     *
     * @param string $localFilePath 本地文件路径
     * @param string $remotePath COS中的保存路径
     * @return object|bool 成功返回上传结果对象，失败返回false
     */
    public function uploadLocalFile(string $localFilePath, string $remotePath): object|bool
    {
        if (!file_exists($localFilePath)) return false; // 检查本地文件是否存在

        $cosClient = new Client([ // 创建COS客户端
            'region' => $this->region, // 设置区域
            'credentials' => ['secretId' => $this->secretId, 'secretKey' => $this->secretKey], // 设置认证信息
        ]);

        try {
            // 格式化远程路径
            $remotePath = $this->formatRemotePath($remotePath);
            
            $result = $cosClient->putObject([ // 调用SDK上传对象
                'Bucket' => $this->bucket, // 设置存储桶
                'Key' => $remotePath, // 设置已处理的对象键
                'Body' => fopen($localFilePath, 'rb'), // 打开本地文件
            ]);
            return $result; // 返回上传结果
        } catch (Exception $e) {
            return false; // 上传失败返回false
        }
    }

    /**
     * 删除COS上的文件
     *
     * @param string $remotePath COS中的文件路径
     * @return bool 删除成功返回true，失败返回false
     */
    public function deleteFile(string $remotePath): bool
    {
        $cosClient = new Client([ // 创建COS客户端
            'region' => $this->region, // 设置区域
            'credentials' => ['secretId' => $this->secretId, 'secretKey' => $this->secretKey], // 设置认证信息
        ]);

        try {
            // 格式化远程路径
            $remotePath = $this->formatRemotePath($remotePath);
            
            $cosClient->deleteObject([ // 调用SDK删除对象
                'Bucket' => $this->bucket, // 设置存储桶
                'Key' => $remotePath, // 设置已处理的对象键
            ]);
            return true; // 删除成功返回true
        } catch (Exception $e) {
            return false; // 删除失败返回false
        }
    }

    /**
     * 获取文件访问URL
     *
     * @param string $remotePath COS中的文件路径
     * @param int $expires URL有效期（秒）
     * @return string|bool 成功返回访问URL，失败返回false
     */
    public function getFileUrl($remotePath, $expires = 3600): string|bool
    {
        $cosClient = new Client([ // 创建COS客户端
            'region' => $this->region, // 设置区域
            'credentials' => ['secretId' => $this->secretId, 'secretKey' => $this->secretKey], // 设置认证信息
        ]);

        try {
            // 格式化远程路径
            $remotePath = $this->formatRemotePath($remotePath);
            
            $signedUrl = $cosClient->getObjectUrl( // 获取对象访问URL
                $this->bucket, // 存储桶
                $remotePath, // 已处理的对象键
                '+' . $expires . ' seconds' // 设置URL有效期
            );
            return $signedUrl; // 返回签名URL
        } catch (Exception $e) {
            return false; // 失败返回false
        }
    }
}
